除了滑鼠外,鍵盤也是使用者必用的輸入設備。在 Compose for Desktop 裡,以下三個地方會與鍵盤互動有關:
TextField
輸入內容時,透過註冊 onValueChange
事件來更新顯示,讀者可參考 Day 10 的筆記回顧一下。MenuBar
的 Item
元件時,可以透過 shortcut
參數綁定鍵盤快速鍵,讀者可參考 Day 8 的筆記回顧一下。onKeyEvent
或 onPreviewKeyEvent
參數可綁定事件的話可以直接傳入 Event Handler 設定對應的動作。若沒有這兩個參數的話,還是可以透過 Modifier
註冊這兩個事件來綁定鍵盤行為。今天耕讀筆記,會針對第三點的鍵盤事件做研究。
Compose 的 Window
與 Dialog
兩個元件本身就有 onKeyEvent
或 onPreviewKeyEvent
參數可綁定事件,由於事件是直接跟整個視窗或對話框綁定,因此元件不需要是當下的焦點就能隨時觸發。
val keys = remember {
mutableStateMapOf(
"ctrl" to false,
"option" to false,
"shift" to false,
"meta" to false,
)
}
Window(onCloseRequest = ::exitApplication, title = "Keyboard Event Basic Setting", onKeyEvent = {
if (it.type == KeyEventType.KeyDown && (it.key == Key.CtrlLeft || it.key == Key.CtrlRight)) {
keys["ctrl"] = true
}
if (it.type == KeyEventType.KeyDown && (it.key == Key.AltLeft || it.key == Key.AltRight)) {
keys["option"] = true
}
if (it.type == KeyEventType.KeyDown && (it.key == Key.ShiftLeft || it.key == Key.ShiftRight)) {
keys["shift"] = true
}
if (it.type == KeyEventType.KeyDown && (it.key == Key.MetaLeft || it.key == Key.MetaRight)) {
keys["meta"] = true
}
if (it.type == KeyEventType.KeyUp) {
keys["ctrl"] = false
keys["option"] = false
keys["shift"] = false
keys["meta"] = false
}
true
}) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
Row(
modifier = Modifier.fillMaxSize(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "^",
fontSize = 100.sp,
color = if (keys["ctrl"] == true) Color.Red else Color.Gray,
)
Text(
text = "⌥",
fontSize = 100.sp,
color = if (keys["option"] == true) Color.Red else Color.Gray,
)
Text(
text = "⇧",
fontSize = 100.sp,
color = if (keys["shift"] == true) Color.Red else Color.Gray,
)
Text(
text = "⌘",
fontSize = 100.sp,
color = if (keys["meta"] == true) Color.Red else Color.Gray,
)
}
}
}
在上面的例子裡,筆者在畫面上放了三個 Text
表示鍵盤上 Ctrl、Option、Shift 及 Meta 四種按鍵,當 Window
收到 onPreviewKeyEvent
時,會更動 keys
Map 裡的狀態,進而改變 Text
的顏色。所以當按鍵被按下時,對應的 Text
就會亮起來。
Modifier
綁定鍵盤事件若元件沒有事件參數可供綁定,還是可以透過 Modifier
為元件綁定鍵盤事件。鍵盤事件有兩個:onKeyEvent
和 onPreviewKeyEvent
,一般來說會傾向使用 onPreviewKeyEvent
來防止事件被子元件觸發。
var text by remember { mutableStateOf("") }
var sent by remember { mutableStateOf(false) }
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
TextField(
value = text,
onValueChange = { text = it },
singleLine = true,
modifier = Modifier.onPreviewKeyEvent {
if (it.key == Key.Enter && it.type == KeyEventType.KeyDown) {
text = ""
sent = true
}
if (it.key != Key.Enter && it.type == KeyEventType.KeyDown) {
sent = false
}
false
}
)
Text(
text = if (sent) "Sent!" else ""
)
}
在上面的例子裡,筆者用 Modifier
在 TextField
上註冊 onPreviewKeyEvent
,當 TextField
是焦點且按下 Enter 鍵時,就會將 TextField
內的文字清空,並在下方顯示 Sent! 字樣。而按下其他按鍵後,Sent! 字樣就會被重設。
Key
及 KeyEventType
在綁定鍵盤事件時,會用 Key
及 KeyEventType
來綁定特定的按鍵及狀態。Key
類別已經將鍵盤上所有按鍵設計成屬性方便開發者在 IDE 裡直接選用,而 KeyEventType
一般只會用到兩種:
KeyDown
:當按鍵按下時。KeyUp
:當按鍵抬起時。透過兩個類別,就組合出複雜的鍵盤按法。